/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  pwm_music.c
 *    Description:  This file used to test PWM music.
 *                 
 *        Version:  1.0.0(2023年05月28日)
 *         Author:  Ling Yun <lingyun@email.com>
 *      ChangeLog:  1, Release initial version on "2023年05月28日 16时51分23秒"
 *                 
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <unistd.h>
#include <string.h>

typedef struct pwm_one_s
{
	unsigned int 	msec;//持续时间
	unsigned int 	freq;//频率
	unsigned char 	duty;//相对占空比,百分比*100
}pwm_one_t;

/*  使用宏确定使用中音还是高低音 */ 
#define CX	CM
static char pwm_path[100];

/*低中高音频率*/
static const unsigned short CL[8] = {0,131,147,165,175,196,211,248};
static const unsigned short CM[8] = {0,262,294,330,350,393,441,495};
static const unsigned short CH[8] = {0,525,589,661,700,786,882,990};

/*小星星曲子*/
static unsigned short songP[] = {
	    CX[1], CX[5], CX[5], CX[5], CX[6], CX[6], CX[5], CX[0],
	    CX[4], CX[4], CX[3], CX[3], CX[2], CX[2], CX[1], CX[0],
		CX[5], CX[5], CX[4], CX[4], CX[3], CX[3], CX[2], CX[0],
};

static int pwm_config(const char *attr,const char *val);
static inline void msleep(unsigned long ms);
static int pwm_ring_one(pwm_one_t *pwm_ring);

int main(int argc,char *argv[])
{
	char temp[100];
	int fd = -1;
	int i=0;
	pwm_one_t pwm_one_test;

	//校验传参
	if(2 != argc)
	{
		printf("usage:%s <id> \n",argv[0]);
		exit(-1);/*  exit(0) 表示进程正常退出 exit(非0)表示异常退出*/
	}

	//打印配置信息
	printf("PWM config:id<%s>\n",argv[1]);

	 /*  导出pwm 首先确定最终导出的文件夹路径*/
	memset(pwm_path,0,sizeof(pwm_path));
	snprintf(pwm_path,sizeof(pwm_path),"/sys/class/pwm/pwmchip%s/pwm0",argv[1]);

	//如果pwm0目录不存在, 则导出
	memset(temp,0,sizeof(temp));
	if(access(pwm_path,F_OK))
	{
		snprintf(temp,sizeof(temp),"/sys/class/pwm/pwmchip%s/export",argv[1]);
		if(0 > (fd = open(temp,O_WRONLY)))
		{
			printf("open pwmchip%s error\n",argv[1]);
			exit(-1);
		}
		//导出pwm0文件
		if(1 != write(fd,"0",1))
		{
			printf("write '0' to pwmchip%s/export error\n",argv[1]);
			close(fd);
			exit(-2);
		}
		close(fd);
	}
	
	pwm_one_test.freq = 1000; //1khz
	pwm_one_test.duty = 50; //50%
	pwm_one_test.msec = 100;
	pwm_config("enable","1");
	for(i=0;i<sizeof(songP)/sizeof(songP[0]);i++)
	{
		if(songP[i] == 0)
		{
			pwm_one_test.duty = 0;
		}
		else
		{
			pwm_one_test.duty = 15;//越大音量越大
			pwm_one_test.freq = songP[i];
		}
		pwm_one_test.msec = 300;
		pwm_ring_one(&pwm_one_test);
	}
	pwm_config("enable","0");
	return 0;
}

/* pwm 配置函数 attr：属性文件名字 
 *  *  val：属性的值(字符串)
 *  */
static int pwm_config(const char *attr,const char *val)
{
	char file_path[102];
	int len;
	int fd = -1;

	if(attr == NULL || val == NULL)
	{
		printf("[%s] argument error\n",__FUNCTION__);
		return -1;
	}

	memset(file_path,0,sizeof(file_path));
	snprintf(file_path, sizeof(file_path), "%s/%s", pwm_path, attr);
	if(0 >(fd = open(file_path,O_WRONLY)))
	{
		printf("[%s] open %s error\n",__FUNCTION__,file_path);
		return fd;
	}

	len = strlen(val);
	if(len != write(fd,val,len))
	{
		printf("[%s] write %s to %s error\n",__FUNCTION__,val,file_path);
		close(fd);
		return -2;
	}
	
	close(fd);//关闭文件
	return 0;
}

//ms级休眠函数
static inline void msleep(unsigned long ms)
{
	struct timespec cSleep;
	unsigned long ulTmp;

	cSleep.tv_sec = ms / 1000;
	if(cSleep.tv_sec == 0)
	{
		ulTmp = ms * 10000;
		cSleep.tv_nsec = ulTmp * 100;
	}
	else
	{
		cSleep.tv_nsec = 0;
	}

	nanosleep(&cSleep,0);
}

//pwm蜂鸣器响一次声音
static int pwm_ring_one(pwm_one_t *pwm_ring)
{
	unsigned long period = 0;
	unsigned long duty_cycle = 0;

	char period_str[20] = {};
	char duty_cycle_str[20] = {};
	
	if( !pwm_ring || pwm_ring->duty > 100)
	{
		printf("[INFO] %s argument error.\n",__FUNCTION__);
		return -1;
	}

	period = (unsigned long)((1.f / (double)pwm_ring->freq) * 1e9);//ns单位
	duty_cycle = (unsigned long)(((double)pwm_ring->duty / 100.f) * (double)period);//ns单位

	snprintf(period_str,sizeof(period_str),"%ld",period);
	snprintf(duty_cycle_str,sizeof(duty_cycle_str),"%ld",duty_cycle);

	// printf("period = %sns, duty_cycle_str = %sns\n", period_str, duty_cycle_str);

	//设置pwm频率和周期
	if (pwm_config("period", period_str))
	{
		printf("pwm_config period failure.\n");
		return -1;
	}
	if (pwm_config("duty_cycle",duty_cycle_str))
	{
		printf("pwm_config duty_cycle failure.\n");
		return -2;
	}

	msleep(pwm_ring->msec);
	//设置占空比为0 蜂鸣器无声音
	if (pwm_config("duty_cycle","0"))
	{
		printf("pwm_config duty_cycle failure.\n");
		return -3;
	}

	msleep(20);
	return 0;
}
